package cs275.ants.graph.base;

import java.util.HashSet;
import java.util.Set;

import junit.framework.TestCase;

import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;

public class GraphTest extends TestCase {

	private Graph emptyGraph;
	private Graph graph2;
	private Graph graph;
	private GraphVertex neighbor1;
	private GraphVertex neighbor2;
	private GraphVertex neighbor3;
	private GraphEdge edge1;
	private GraphEdge edge2;
	private GraphEdge edge3;
	private GraphVertex vertex;

	protected void setUp() throws Exception {
		super.setUp();
		graph = new Graph();
		neighbor1 = new GraphVertex(1,0);
		neighbor2 = new GraphVertex(0,1);
		neighbor3 = new GraphVertex(1,2);
		vertex = new GraphVertex(1,1);
		edge1 = new GraphEdge(neighbor1, vertex, 0);
		edge2 = new GraphEdge(neighbor2, vertex, 0);
		edge3 = new GraphEdge(neighbor3, vertex, 0);
		
		
		emptyGraph = new Graph();
		graph2 = new Graph();
	}

	protected void tearDown() throws Exception {
		super.tearDown();
	}

	public void testEmptyGraph() {
		boolean isEmpty = emptyGraph.getAllVertices().isEmpty();
		assertTrue(isEmpty);
	}

	public void testAddRemoveVertex() {
		GraphVertex v1 = new GraphVertex(0,0);
		GraphVertex v2 = new GraphVertex(0,1);

		assertFalse(graph2.removeVertex(v1));
		assertFalse(graph2.removeVertex(v2));

		assertTrue(graph2.addVertex(v1));
		assertFalse(graph2.addVertex(v1));
		Set<GraphVertex> allVertices = graph2.getAllVertices();
		assertTrue(allVertices.contains(v1));
		assertFalse(allVertices.contains(v2));

		assertTrue(graph2.addVertex(v2));
		Set<GraphVertex> all2 = graph2.getAllVertices();
		assertTrue(all2.contains(v1));
		assertTrue(all2.contains(v2));

		// remove
		assertTrue(graph2.removeVertex(v2));
		Set<GraphVertex> all3 = graph2.getAllVertices();
		assertTrue(all3.contains(v1));
		assertFalse(all3.contains(v2));

		assertTrue(graph2.removeVertex(v1));
		Set<GraphVertex> all4 = graph2.getAllVertices();
		assertFalse(all4.contains(v1));
		assertFalse(all4.contains(v2));
	}

	public void testAddRemoveEdge() {
		// an edge between nodes not exist in graph
		GraphVertex node1 = new GraphVertex(0,0);
		GraphVertex node2 = new GraphVertex(0,1);
		GraphEdge edge1 = new GraphEdge(node1, node2, 10);
		assertNull(emptyGraph.addEdge(edge1));

		// create a redundant edge for testing
		GraphVertex node3 = new GraphVertex(1,0);
		GraphVertex node4 = new GraphVertex(1,1);
		GraphEdge testEdge = new GraphEdge(node3, node4, 1000);
		assertFalse(graph2.removeEdge(testEdge));

		graph2.addVertex(node1);
		graph2.addVertex(node2);
		GraphEdge edge2 = new GraphEdge(node1, node2, 10);
		GraphEdge newEdge = graph2.addEdge(edge2);

		HashSet<GraphEdge> set1 = graph2.getEdgesByVertex(node1);
		assertTrue(set1.contains(newEdge));
		HashSet<GraphEdge> set2 = graph2.getEdgesByVertex(node1);
		assertTrue(set2.contains(newEdge));

		// test again after removal
		assertTrue(graph2.removeEdge(newEdge));
		HashSet<GraphEdge> set3 = graph2.getEdgesByVertex(node1);
		assertFalse(set3.contains(newEdge));
		HashSet<GraphEdge> set4 = graph2.getEdgesByVertex(node1);
		assertFalse(set4.contains(newEdge));
	}

	public void testGetEdgeByVertex() {
		GraphVertex node1 = new GraphVertex(0,0);
		GraphVertex node2 = new GraphVertex(0,1);
		GraphVertex node3 = new GraphVertex(1,0);
		GraphVertex node4 = new GraphVertex(1,1);

		graph2.addVertex(node1);
		graph2.addVertex(node2);
		graph2.addVertex(node3);
		GraphEdge newEdge = new GraphEdge(node1, node2, 100);

		graph2.addEdge(newEdge);
		HashSet<GraphEdge> set1 = graph2.getEdgesByVertex(node1);
		assertTrue(set1.contains(newEdge));
		HashSet<GraphEdge> set2 = graph2.getEdgesByVertex(node1);
		assertTrue(set2.contains(newEdge));

		HashSet<GraphEdge> set3 = graph2.getEdgesByVertex(node2);
		assertTrue(set3.contains(newEdge));
		HashSet<GraphEdge> set4 = graph2.getEdgesByVertex(node2);
		assertTrue(set4.contains(newEdge));

		HashSet<GraphEdge> set5 = graph2.getEdgesByVertex(node3);
		assertTrue(set5.isEmpty());

		HashSet<GraphEdge> set6 = graph2.getEdgesByVertex(node4);
		assertNull(set6);
	}

	public void testGetNeighborsByVertex() {
		graph.addVertices(vertex, neighbor1, neighbor2, neighbor3);
		graph.addEdges(edge1, edge2, edge3);

		HashSet<GraphVertex> neighbors = graph.getNeighborsByVertex(vertex);
		assertTrue(neighbors.contains(neighbor1));
		assertTrue(neighbors.contains(neighbor2));
		assertTrue(neighbors.contains(neighbor3));
		assertFalse(neighbors.contains(vertex));
	}
	// serialization test 
	public void testIsSerializable() throws Exception {
		
		// create a graph
		Graph tGraph = new Graph(); 
		GraphVertex vertex1 = new GraphVertex(0,0);
		GraphVertex vertex2 = new GraphVertex(0,1);
		GraphVertex vertex3 = new GraphVertex(0,2);		
		tGraph.addVertex(vertex1);
		tGraph.addVertex(vertex2);
		tGraph.addVertex(vertex3);
		GraphEdge newEdge = new GraphEdge(vertex2, vertex3, 23);
		tGraph.addEdge(newEdge);

	    ByteArrayOutputStream out = new ByteArrayOutputStream();
	    ObjectOutputStream oos = new ObjectOutputStream(out);
	    oos.writeObject(tGraph);
	    oos.close();
	    assertTrue(out.toByteArray().length > 0);

	}
	
	public void testAddEdges() {
		graph.addVertices(vertex, neighbor1, neighbor2, neighbor3);
		graph.addEdges(edge1, edge2, edge3);

		HashSet<GraphEdge> edges = graph.getEdgesByVertex(vertex);
		assertTrue(edges.contains(edge1));
		assertTrue(edges.contains(edge2));
		assertTrue(edges.contains(edge3));
		assertEquals(3, edges.size());
	}

	public void testAddVertices() {
		graph.addVertices(vertex, neighbor1, neighbor2, neighbor3);
		graph.addEdges(edge1, edge2, edge3);
		
		Set<GraphVertex> vertices = graph.getAllVertices();
		assertEquals(4, vertices.size());
		assertTrue(vertices.contains(vertex));
		assertTrue(vertices.contains(neighbor1));
		assertTrue(vertices.contains(neighbor2));
		assertTrue(vertices.contains(neighbor3));
	}
}
